package game_frame

import (
	"gamesdk/common/log"
	"gamesdk/global"
	"gamesdk/lib/net"
	"gamesdk/msg"
	"sync"
	"time"

	"github.com/golang/protobuf/proto"
	"google.golang.org/grpc"
)

type Client struct {
	grpc     msg.RoomClient
	client   *net.Client
	grpcConn *grpc.ClientConn
	room     *Room
	id       string
	user     map[int64]*User
	lock     sync.RWMutex
	wait     sync.WaitGroup
}

func NewClient(tcp_ip, grpc_url, id string, heartbeart int, room *Room) *Client {
	conn, err := grpc.Dial(grpc_url, grpc.WithInsecure())
	if err != nil {
		log.Error("can't connect: " + grpc_url)
		return nil
	}

	grpc := msg.NewRoomClient(conn)
	client := &Client{room: room, grpc: grpc, id: id, grpcConn: conn, user: make(map[int64]*User)}
	cli := net.NewTcpClient(tcp_ip, heartbeart, client)
	if cli == nil {
		log.Error("can't connect: " + tcp_ip)
		return nil
	}
	client.client = cli
	go cli.Start()
	room.conns.Store(id, client)
	config := &msg.C2SConfig{
		//服务器Token,自动生成
		Token: client.room.token,
		//1表示初始化命令
		Cmd: 1,
		//逻辑服务启动了多少张桌子
		TableNum: int32(global.G_Config.TableNum),
		//每张桌子最大容纳的人
		Max: int32(global.G_Config.Max),
		//每张桌子最小容纳的人
		Min: int32(global.G_Config.Min),
	}
	//遍历所有桌子
	client.room.tableManager.Traverse(func(key int, value *Table) bool {
		if value.roomID != -1 {
			//目前逻辑服务存在运行的房间ID
			config.Rooms = append(config.Rooms, value.roomID)
		}
		return true
	})
	buf, err := proto.Marshal(config)
	if err != nil {
		log.Error("proto失败: [%v] ", err)
		cli.Exit()
		return nil
	}
	sendBody := &msg.C2SNoticeMsg{
		//表示建链初始化消息
		Cmd: int32(msg.CMDKIND_VOID1),
		//C2SConfig 配置消息的pb包
		Body: buf,
		//服务器的token
		Token: client.room.token,
		//消息ID 时间戳
		MsgId: time.Now().UnixNano(),
	}
	log.Info("发送配置%v", sendBody)
	dd, err := proto.Marshal(sendBody)
	if err != nil {
		log.Error(err.Error())
		cli.Exit()
		return nil
	}
	cli.Write(dd)
	return client
}

func (this *Client) RecvMsg(buff []byte) {
	resp := &msg.S2CNoticeMsg{}
	err := proto.Unmarshal(buff, resp)
	if err != nil {
		log.Error(err.Error())
		return
	}
	//log.Debug("收到消息%v", resp)
	if resp.Cmd == int32(msg.CMDKIND_SHUTDOWN) {
		log.Warn("收到服务器关闭消息,进入关闭流程")
		this.lock.RLock()
		for _, user := range this.user {
			this.room.funcSysOut(user.GetId())
		}
		this.lock.RUnlock()
		this.wait.Wait()
		sendMsg := &msg.C2SNoticeMsg{Cmd: int32(msg.CMDKIND_SHUTDOWN), UserId: 0, Body: nil, Token: this.room.token, MsgId: time.Now().UnixNano()}
		this.funcWrite(sendMsg)
	}
	//log.Warn("收到%s的消息%v", this.id, resp)
	this.room.acceptMsg <- &AcceptMsg{client: this, s2cNoticeMsg: resp}
}

func (this *Client) AddUser(user *User) {
	this.lock.Lock()
	defer this.lock.Unlock()
	_, ok := this.user[user.GetId()]
	if !ok {
		this.user[user.GetId()] = user
		this.wait.Add(1)
	}
}

func (this *Client) DelUser(user *User) {
	this.lock.Lock()
	defer this.lock.Unlock()
	_, ok := this.user[user.GetId()]
	if ok {
		this.user[user.GetId()] = user
		delete(this.user, user.GetId())
		this.wait.Done()
	}
}

func (this *Client) funcWrite(body *msg.C2SNoticeMsg) {
	defer log.Trace()()
	body.MsgId = time.Now().UnixNano()
	sendData, err := proto.Marshal(body)
	if err != nil {
		log.Error(err.Error())
		return
	}
	this.client.Write(sendData)
}

//心跳
func (this *Client) HearBeat() {
	config := &msg.C2SConfig{Token: this.room.token, Cmd: 1, TableNum: int32(global.G_Config.TableNum), Max: int32(global.G_Config.Max), Min: int32(global.G_Config.Min)}
	//遍历所有桌子
	this.room.tableManager.Traverse(func(key int, value *Table) bool {
		if value.roomID != -1 {
			config.Rooms = append(config.Rooms, value.roomID)
		}
		return true
	})
	buf, err := proto.Marshal(config)
	if err != nil {
		log.Error("proto失败: [%v] ", err)
		return
	}
	sendBody := &msg.C2SNoticeMsg{Cmd: int32(msg.CMDKIND_VOID1), Body: buf, Token: this.room.token, MsgId: time.Now().UnixNano()}
	this.funcWrite(sendBody)
}

func (this *Client) Exit() {
	if this.grpcConn != nil {
		this.grpcConn.Close()
	}
	this.room.conns.Delete(this.id)
	this.lock.RLock()
	for _, user := range this.user {
		this.room.funcSysOut(user.GetId())
	}
	this.lock.RUnlock()
	log.Error("connect is close %s", this.id)
}

func (this *Client) Error(err error) {
	log.Error("connect is err %s", err.Error())
}

//初始化
func (this *Client) Init() {
}
